<template>
  <div v-if="easyFlowVisible" style="height: calc(100vh - 110px);overflow: hidden;">
    <el-row>
      <!--顶部工具菜单-->
      <el-col :span="24">
        <div class="ef-tooltar">
          <el-link type="primary" :underline="false">{{ data.name }}</el-link>
          <el-divider direction="vertical" />
          <el-button type="text" icon="el-icon-delete" size="large" :disabled="!activeElement.type" @click="deleteElement" />
          <el-divider direction="vertical" />
          <!-- <el-button type="text" icon="el-icon-download" size="large" @click="downloadData" />
          <el-divider direction="vertical" /> -->
          <el-button type="text" icon="el-icon-plus" size="large" @click="zoomAdd" />
          <el-divider direction="vertical" />
          <el-button type="text" icon="el-icon-minus" size="large" @click="zoomSub" />
          <div style="float: right;margin-right: 5px">
            <el-button v-loading="saveLoading" type="info" plain round icon="el-icon-document" size="mini" @click="dataInfo">保存流程</el-button>
            <!-- <el-button type="primary" plain round icon="el-icon-refresh" size="mini" @click="dataReloadA">切换流程A</el-button>
            <el-button type="primary" plain round icon="el-icon-refresh" size="mini" @click="dataReloadB">切换流程B</el-button>
            <el-button type="primary" plain round icon="el-icon-refresh" size="mini" @click="dataReloadC">切换流程C</el-button>
            <el-button type="primary" plain round icon="el-icon-refresh" size="mini" @click="dataReloadD">自定义样式</el-button>
            <el-button type="info" plain round icon="el-icon-document" size="mini" @click="openHelp">帮助</el-button> -->
          </div>
        </div>
      </el-col>
    </el-row>
    <div style="display: flex;height: calc(100% - 47px);">
      <div style="width: 230px;border-right: 1px solid #dce3e8;">
        <node-menu ref="nodeMenu" @addNode="addNode" />
      </div>
      <div id="efContainer" ref="efContainer" v-flowDrag class="container">
        <template v-for="node in data.nodeList">
          <flow-node
            :id="node.nodeId"
            :key="node.nodeId"
            :node="node"
            :active-element="activeElement"
            @changeNodeSite="changeNodeSite"
            @nodeRightMenu="nodeRightMenu"
            @clickNode="clickNode"
          />
        </template>
        <!-- 给画布一个默认的宽度和高度 -->
        <div style="position:absolute;top: 2000px;left: 2000px;">&nbsp;</div>
      </div>
      <!-- 右侧表单 -->
      <div style="width: 300px;border-left: 1px solid #dce3e8;background-color: #FBFBFB">
        <flow-node-form ref="nodeForm" @setLineLabel="setLineLabel" @repaintEverything="repaintEverything" />
      </div>
    </div>
    <!-- 流程数据详情 -->
    <flow-info v-if="flowInfoVisible" ref="flowInfo" :data="data" />
    <flow-help v-if="flowHelpVisible" ref="flowHelp" />
  </div>

</template>

<script>
import { edit as editActionFlow, add as addActionFlow } from '@/api/system/actionFlow'
import draggable from 'vuedraggable'
// import { jsPlumb } from 'jsplumb'
// 使用修改后的jsplumb
import './jsplumb'
import { easyFlowMixin } from '@/components/ef/mixins'
import flowNode from '@/components/ef/node'
import nodeMenu from '@/components/ef/node_menu'
import FlowInfo from '@/components/ef/info'
import FlowHelp from '@/components/ef/help'
import FlowNodeForm from './node_form'
import lodash from 'lodash'
import { getDataA } from './data_A'
import { getDataB } from './data_B'
import { getDataC } from './data_C'
import { getDataD } from './data_D'

export default {
  components: {
    // eslint-disable-next-line vue/no-unused-components
    draggable, flowNode, nodeMenu, FlowInfo, FlowNodeForm, FlowHelp
  },
  directives: {
    'flowDrag': {
      bind(el, binding, vnode, oldNode) {
        if (!binding) {
          return
        }
        el.onmousedown = (e) => {
          if (e.button === 2) {
            // 右键不管
            return
          }
          //  鼠标按下，计算当前原始距离可视区的高度
          let disX = e.clientX
          let disY = e.clientY
          el.style.cursor = 'move'

          document.onmousemove = function(e) {
            // 移动时禁止默认事件
            e.preventDefault()
            const left = e.clientX - disX
            disX = e.clientX
            el.scrollLeft += -left

            const top = e.clientY - disY
            disY = e.clientY
            el.scrollTop += -top
          }

          document.onmouseup = function(e) {
            el.style.cursor = 'auto'
            document.onmousemove = null
            document.onmouseup = null
          }
        }
      }
    }
  },
  // 一些基础配置移动该文件中
  mixins: [easyFlowMixin],
  props: {
    flowData: {
      type: Object,
      default: null
    }
  },
  data() {
    return {
      saveLoading: false,
      // jsPlumb 实例
      jsPlumb: null,
      // 控制画布销毁
      easyFlowVisible: true,
      // 控制流程数据显示与隐藏
      flowInfoVisible: false,
      // 是否加载完毕标志位
      loadEasyFlowFinish: false,
      flowHelpVisible: false,
      // 数据
      data: {},
      // 激活的元素、可能是节点、可能是连线
      activeElement: {
        // 可选值 node 、line
        type: undefined,
        // 节点ID
        nodeId: undefined,
        // 连线ID
        sourceId: undefined,
        targetId: undefined,
        name: ''
      },
      zoom: 1
    }
  },
  mounted() {
    // eslint-disable-next-line no-undef
    this.jsPlumb = jsPlumb.getInstance()
    this.$nextTick(() => {
      // 默认加载流程A的数据、在这里可以根据具体的业务返回符合流程数据格式的数据即可
      // console.log(getDataB())
      // this.dataReload(getDataB())
      console.log(this.flowData)
      this.dataReload({
        name: this.flowData ? this.flowData.name : '',
        nodeList: this.flowData ? JSON.parse(this.flowData.nodeList) : [],
        lineList: this.flowData ? JSON.parse(this.flowData.lineList) : []
      })
    })
  },
  methods: {
    // 返回唯一标识
    getUUID() {
      return Math.random().toString(36).substr(3, 10)
    },
    jsPlumbInit() {
      this.jsPlumb.ready(() => {
        // 导入默认配置
        this.jsPlumb.importDefaults(this.jsplumbSetting)
        // 会使整个jsPlumb立即重绘。
        this.jsPlumb.setSuspendDrawing(false, true)
        // 初始化节点
        this.loadEasyFlow()
        // 单点击了连接线, https://www.cnblogs.com/ysx215/p/7615677.html
        this.jsPlumb.bind('click', (conn, originalEvent) => {
          this.activeElement.type = 'line'
          this.activeElement.sourceId = conn.sourceId
          this.activeElement.targetId = conn.targetId
          this.$refs.nodeForm.lineInit({
            from: conn.sourceId,
            to: conn.targetId,
            label: conn.getLabel()
          })
        })
        // 连线
        this.jsPlumb.bind('connection', (evt) => {
          const from = evt.source.id
          const to = evt.target.id
          if (this.loadEasyFlowFinish) {
            this.data.lineList.push({
              from: from, to: to
            })
          }
        })

        // 删除连线回调
        this.jsPlumb.bind('connectionDetached', (evt) => {
          this.deleteLine(evt.sourceId, evt.targetId)
        })

        // 改变线的连接节点
        this.jsPlumb.bind('connectionMoved', (evt) => {
          this.changeLine(evt.originalSourceId, evt.originalTargetId)
        })

        // 连线右击
        this.jsPlumb.bind('contextmenu', (evt) => {
          console.log('contextmenu', evt)
        })

        // 连线
        this.jsPlumb.bind('beforeDrop', (evt) => {
          const from = evt.sourceId
          const to = evt.targetId
          if (from === to) {
            this.$message.error('节点不支持连接自己')
            return false
          }
          if (this.hasLine(from, to)) {
            this.$message.error('该关系已存在,不允许重复创建')
            return false
          }
          if (this.hashOppositeLine(from, to)) {
            this.$message.error('不支持两个节点之间连线回环')
            return false
          }
          this.$message.success('连接成功')
          return true
        })

        // beforeDetach
        this.jsPlumb.bind('beforeDetach', (evt) => {
          console.log('beforeDetach', evt)
        })
        this.jsPlumb.setContainer(this.$refs.efContainer)
      })
    },
    // 加载流程图
    loadEasyFlow() {
      // 初始化节点
      for (var i = 0; i < this.data.nodeList.length; i++) {
        const node = this.data.nodeList[i]
        // 设置源点，可以拖出线连接其他节点
        this.jsPlumb.makeSource(node.nodeId, lodash.merge(this.jsplumbSourceOptions, {}))
        // // 设置目标点，其他源点拖出的线可以连接该节点
        this.jsPlumb.makeTarget(node.nodeId, this.jsplumbTargetOptions)
        if (!node.viewOnly) {
          this.jsPlumb.draggable(node.nodeId, {
            containment: 'parent',
            stop: function(el) {
              // 拖拽节点结束后的对调
              console.log('拖拽结束: ', el)
            }
          })
        }
      }
      // 初始化连线
      for (let i = 0; i < this.data.lineList.length; i++) {
        const line = this.data.lineList[i]
        var connParam = {
          source: line.from,
          target: line.to,
          label: line.label ? line.label : '',
          connector: line.connector ? line.connector : '',
          anchors: line.anchors ? line.anchors : undefined,
          paintStyle: line.paintStyle ? line.paintStyle : undefined
        }
        this.jsPlumb.connect(connParam, this.jsplumbConnectOptions)
      }
      this.$nextTick(function() {
        this.loadEasyFlowFinish = true
      })
    },
    // 设置连线条件
    setLineLabel(from, to, label) {
      var conn = this.jsPlumb.getConnections({
        source: from,
        target: to
      })[0]
      if (!label || label === '') {
        conn.removeClass('flowLabel')
        conn.addClass('emptyFlowLabel')
      } else {
        conn.addClass('flowLabel')
      }
      conn.setLabel({
        label: label
      })
      this.data.lineList.forEach(function(line) {
        if (line.from === from && line.to === to) {
          line.label = label
        }
      })
    },
    // 删除激活的元素
    deleteElement() {
      console.log(this.activeElement)
      if (this.activeElement.type === 'node') {
        this.deleteNode(this.activeElement.nodeId, this.activeElement.name)
      } else if (this.activeElement.type === 'line') {
        this.$confirm('确定删除所点击的线吗?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          var conn = this.jsPlumb.getConnections({
            source: this.activeElement.sourceId,
            target: this.activeElement.targetId
          })[0]
          this.jsPlumb.deleteConnection(conn)
        }).catch(() => {
        })
      }
    },
    // 删除线
    deleteLine(from, to) {
      this.data.lineList = this.data.lineList.filter(function(line) {
        if (line.from === from && line.to === to) {
          return false
        }
        return true
      })
    },
    // 改变连线
    changeLine(oldFrom, oldTo) {
      this.deleteLine(oldFrom, oldTo)
    },
    // 改变节点的位置
    changeNodeSite(data) {
      for (var i = 0; i < this.data.nodeList.length; i++) {
        const node = this.data.nodeList[i]
        if (node.id === data.nodeId) {
          node.left = data.left
          node.top = data.top
        }
      }
    },
    /**
             * 拖拽结束后添加新的节点
             * @param evt
             * @param nodeMenu 被添加的节点对象
             * @param mousePosition 鼠标拖拽结束的坐标
             */
    addNode(evt, nodeMenu, mousePosition) {
      var screenX = evt.originalEvent.clientX; var screenY = evt.originalEvent.clientY
      const efContainer = this.$refs.efContainer
      var containerRect = efContainer.getBoundingClientRect()
      var left = screenX; var top = screenY
      // 计算是否拖入到容器中
      if (left < containerRect.x || left > containerRect.width + containerRect.x || top < containerRect.y || containerRect.y > containerRect.y + containerRect.height) {
        this.$message.error('请把节点拖入到画布中')
        return
      }
      left = left - containerRect.x + efContainer.scrollLeft
      top = top - containerRect.y + efContainer.scrollTop
      // 居中
      left -= 85
      top -= 16
      var nodeId = this.getUUID()
      // 动态生成名字
      var origName = nodeMenu.name
      var nodeName = origName
      var index = 1
      while (index < 10000) {
        var repeat = false
        for (var i = 0; i < this.data.nodeList.length; i++) {
          const node = this.data.nodeList[i]
          if (node.name === nodeName) {
            nodeName = origName + index
            repeat = true
          }
        }
        if (repeat) {
          index++
          continue
        }
        break
      }
      var node = {
        nodeId: nodeId,
        name: nodeName,
        job: nodeMenu.job,
        type: nodeMenu.type,
        left: left + 'px',
        top: top + 'px',
        ico: nodeMenu.ico,
        state: 'success',
        deptId: undefined,
        roleId: '',
        userId: '',
        userName: ''

      }
      /**
                 * 这里可以进行业务判断、是否能够添加该节点
                 */
      this.data.nodeList.push(node)
      this.$nextTick(function() {
        this.jsPlumb.makeSource(nodeId, this.jsplumbSourceOptions)
        this.jsPlumb.makeTarget(nodeId, this.jsplumbTargetOptions)
        this.jsPlumb.draggable(nodeId, {
          containment: 'parent',
          stop: function(el) {
            // 拖拽节点结束后的对调
            console.log('拖拽结束: ', el)
          }
        })
      })
    },
    /**
     * 删除节点
     * @param nodeId 被删除节点的ID
     */
    deleteNode(nodeId, name) {
      this.$confirm('确定要删除节点' + name + '?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
        closeOnClickModal: false
      }).then(() => {
        /**
         * 这里需要进行业务判断，是否可以删除
         */
        this.data.nodeList = this.data.nodeList.filter(function(node) {
          if (node.nodeId === nodeId) {
            // 伪删除，将节点隐藏，否则会导致位置错位
            // node.show = false
            return false
          }
          return true
        })
        this.$nextTick(function() {
          this.jsPlumb.removeAllEndpoints(nodeId)
        })
      }).catch(() => {
      })
      return true
    },
    clickNode(node) {
      this.activeElement.type = 'node'
      this.activeElement.nodeId = node.nodeId
      this.$refs.nodeForm.nodeInit(this.data, node.nodeId)
    },
    // 是否具有该线
    hasLine(from, to) {
      for (var i = 0; i < this.data.lineList.length; i++) {
        var line = this.data.lineList[i]
        if (line.from === from && line.to === to) {
          return true
        }
      }
      return false
    },
    // 是否含有相反的线
    hashOppositeLine(from, to) {
      return this.hasLine(to, from)
    },
    nodeRightMenu(nodeId, evt) {
      this.menu.show = true
      this.menu.curNodeId = nodeId
      this.menu.left = evt.x + 'px'
      this.menu.top = evt.y + 'px'
    },
    repaintEverything() {
      this.jsPlumb.repaint()
    },
    // 流程数据信息
    dataInfo() {
      // this.flowInfoVisible = true
      // this.$nextTick(function() {
      //   this.$refs.flowInfo.init()
      // })
      console.log(this.data)
      const lineList = JSON.parse(JSON.stringify(this.data.lineList))
      const nodeList = JSON.parse(JSON.stringify(this.data.nodeList))
      //
      if (nodeList.length < 2) {
        this.$message({
          type: 'error',
          message: '请选择2个及以上环节'
        })
        return
      }
      let flag = false
      for (let i = 0, len = nodeList.length; i < len; i++) {
        if (nodeList[i].type === 'end') {
          flag = true
        }
      }
      if (!flag) {
        this.$message({
          type: 'error',
          message: '请选择结束节点'
        })
        return
      }
      if (lineList.length < (nodeList.length - 1)) {
        this.$message({
          type: 'error',
          message: '请完成环节连线'
        })
        return
      }
      const submitData = {
        name: this.$refs.nodeForm.processName,
        lineList: JSON.stringify(lineList),
        nodeList: JSON.stringify(nodeList)
      }
      // 排序
      // 找点开始点
      let startId
      let prevId
      for (let i = 0, len = nodeList.length; i < len; i++) {
        if (nodeList[i].type === 'start') {
          startId = nodeList[i].nodeId
          nodeList[i].prevNode = ''
          nodeList[i].nodeNo = 1
          break
        }
      }
      for (let i = 0, len = lineList.length; i < len; i++) {
        if (lineList[i].from === startId) {
          startId = lineList[i].to
          prevId = lineList[i].from
          break
        }
      }
      // 从开始点开始排序
      function sortLine(sid, index, pid) {
        console.log(sid, index, pid)
        console.log(index, nodeList.length)
        if (index > nodeList.length) {
          return
        }
        for (let i = 0, len = lineList.length; i < len; i++) {
          const line = lineList[i]
          if (sid === line.from) {
            for (let j = 0; j < nodeList.length; j++) {
              if (line.from === nodeList[j].nodeId) {
                nodeList[j].nodeNo = index
                nodeList[j].prevNode = pid
                prevId = line.from
                break
              }
            }
            sortLine(line.to, ++index, line.from)
          }
        }
      }
      // 中间部分
      sortLine(startId, 2, prevId)
      // 结束点
      for (let i = 0, len = nodeList.length; i < len; i++) {
        if (nodeList[i].type === 'end') {
          nodeList[i].prevNode = prevId
          nodeList[i].nodeNo = nodeList.length
          break
        }
      }
      // for (let i = 0, len = lineList.length; i < len; i++) {
      //   const line = lineList[i]
      //   for (let j = 0; i < nodeList.length; j++) {
      //     if (line.from === nodeList[j].nodeId) {
      //       nodeList[j].nodeNo = ++i
      //       break
      //     }
      //   }
      // }
      const actionFlowNodes = nodeList.map(item => {
        const id = item.nodeId
        let to
        for (let i = 0, len = lineList.length; i < len; i++) {
          const line = lineList[i]
          // console.log(line.from === id)
          if (line.from === id) {
            to = line.to
            break
          }
        }
        return {
          ...item,
          id: item.nodeId,
          nextNode: to,
          isEnd: item.type === 'end' ? 1 : 0
        }
      })
      // console.log(actionFlowNodes)
      submitData.actionFlowNodes = actionFlowNodes
      console.log(submitData)
      this.saveLoading = true
      if (this.flowData && this.flowData.id) {
        submitData.id = this.flowData.id
        editActionFlow(submitData).then(result => {
          console.log(result)
          this.$message({
            type: 'success',
            message: '保存成功'
          })
          this.pageBack()
        }).catch(() => {
          this.saveLoading = false
        })
      } else {
        addActionFlow(submitData).then(result => {
          console.log(result)
          this.$message({
            type: 'success',
            message: '保存成功'
          })
          this.pageBack()
        }).catch(() => {
          this.saveLoading = false
        })
      }
    },
    pageBack() {
      this.$router.push('/data-management/process')
    },
    // 加载流程图
    dataReload(data) {
      this.easyFlowVisible = false
      this.data.nodeList = []
      this.data.lineList = []
      this.$nextTick(() => {
        data = lodash.cloneDeep(data)
        this.easyFlowVisible = true
        this.data = data
        this.$nextTick(() => {
          // eslint-disable-next-line no-undef
          this.jsPlumb = jsPlumb.getInstance()
          this.$nextTick(() => {
            this.jsPlumbInit()
            this.$refs.nodeForm.processName = data && data.name
          })
        })
      })
    },
    // 模拟载入数据dataA
    dataReloadA() {
      this.dataReload(getDataA())
    },
    // 模拟载入数据dataB
    dataReloadB() {
      this.dataReload(getDataB())
    },
    // 模拟载入数据dataC
    dataReloadC() {
      this.dataReload(getDataC())
    },
    // 模拟载入数据dataD
    dataReloadD() {
      this.dataReload(getDataD())
    },
    zoomAdd() {
      if (this.zoom >= 1) {
        return
      }
      const zoom = this.zoom + 0.1
      this.zoom = zoom > 1 ? 1 : zoom
      this.$refs.efContainer.style.transform = `scale(${this.zoom})`
      this.jsPlumb.setZoom(this.zoom)
    },
    zoomSub() {
      if (this.zoom <= 0) {
        return
      }
      this.zoom = this.zoom - 0.1
      this.$refs.efContainer.style.transform = `scale(${this.zoom})`
      this.jsPlumb.setZoom(this.zoom)
    },
    // 下载数据
    downloadData() {
      this.$confirm('确定要下载该流程数据吗？', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
        closeOnClickModal: false
      }).then(() => {
        var datastr = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(this.data, null, '\t'))
        var downloadAnchorNode = document.createElement('a')
        downloadAnchorNode.setAttribute('href', datastr)
        downloadAnchorNode.setAttribute('download', 'data.json')
        downloadAnchorNode.click()
        downloadAnchorNode.remove()
        this.$message.success('正在下载中,请稍后...')
      }).catch(() => {
      })
    },
    openHelp() {
      this.flowHelpVisible = true
      this.$nextTick(function() {
        this.$refs.flowHelp.init()
      })
    }
  }
}
</script>
